import Snake from "./Snake"
import Food from "./Food"
import ScorePanel from "./ScorePanel"
import Keys from "./interface/Keys"
import BaseUtils from "./utils/BaseUtils"

/**
 * 游戏控制器，控制其他的所有类
 */
class GameControl {

  /**
   * 蛇
   */
  private snake: Snake

  /**
   * 食物
   */
  private food: Food

  /**
   * 计分器
   */
  private scorelPanel: ScorePanel

  /**
   * 蛇的移动方向
   */
  private direction: string 
  
  /**
   * 游戏是否结束
   */
  private isLive: boolean

  private readonly keys: Keys = {"ArrowUp": 1, "ArrowDown": 1, "ArrowLeft": 2, "ArrowRight": 2}

  constructor() {
    this.snake = new Snake()
    this.food = new Food()
    this.scorelPanel = new ScorePanel(10, 1)

    this.direction = ""
    this.isLive = true

    this.init()
  }

  /**
   * 初始化
   */
  init() {
    // 绑定键盘事件
    document.addEventListener("keydown", BaseUtils.deBounce(this.keyDownHandler.bind(this)))
    
    this.snakeRun()
  }

  /**
   * 键盘响应方式
   */
  keyDownHandler(event: KeyboardEvent) {
    let key: string = event.key
    
    // 不是可以按的键盘
    if (this.keys[key] === undefined) {
      return
    }

    // 当蛇不是只有蛇头的时候不能往反方向走
    if (this.snake.snakeBody.length != 1 && this.keys[key] === this.keys[this.direction]) {
      return
    }

    this.direction = key
  }

  /**
   * 控制蛇移动
   */
  snakeRun() {
    // 根据方向来使蛇的位置改变
    // 获取蛇的当前坐标
    let x:number = this.snake.X
    let y:number = this.snake.Y
    switch (this.direction) {
      // 向上 top 减少
      case "ArrowUp":
        y -= 10
        break
      // 向下 top 增加
      case "ArrowDown":
        y += 10
        break
      // 向左 left 减少
      case "ArrowLeft":
        x -= 10
        break
      // 向右 left 增加
      case "ArrowRight":
        x += 10
        break
    }

    // 修改蛇的位置
    try {
      this.snake.moveSnake(x, y)
    } catch(e) {
      alert((e as Error).message)
      this.isLive = false
    }

    // 判断有没有吃到食物
    this.checkEat(x, y)

    // 计算写行走的速度， 最慢每300毫秒走一次，最快每30毫秒走一次
    let speed = 300 - (this.scorelPanel.level - 1) * 30
    this.isLive && setTimeout(this.snakeRun.bind(this), speed)
  }

  /**
   * 检查有没有吃到食物
   * @param x 
   * @param y 
   */
  private checkEat(x: number, y: number): void {
    // 蛇的位置等于食物的位置
    if (x === this.food.X && y === this.food.Y) {
      this.eatFood()
    }
  }

  /**
   * 蛇吃到食物
   */
  private eatFood(): void {
    // 重新切换食物的位置
    this.checkFoodLocation()
    // 分数增加
    this.scorelPanel.addScore()
    // 蛇增加一节
    this.snake.addBody()
  }

  /**
   * 重新生成食物并不允许食物与蛇身体重合
   */
  private checkFoodLocation(): void {
    this.food.change()
    // 判断新食物是否在蛇出现的位置
    let body = this.snake.snakeBody
    for (let i = 0; i < body.length; i++) {
      if (this.food.X === body[i].offsetLeft && this.food.Y === body[i].offsetTop) {
        this.checkFoodLocation()
        break
      }
    }
  }
}

export default GameControl